package com.laicunba.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jms.Message;
import javax.jms.TextMessage;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.laicunba.jms.JsonMessage;
import com.laicunba.pojo.ActivityDO;
import com.laicunba.pojo.CashCouponDefDO;
import com.laicunba.pojo.FinancingTargetBidDO;
import com.laicunba.pojo.RegistryDO;
import com.laicunba.pojo.ScriptDO;
import com.laicunba.pojo.UserDO;
import com.laicunba.pojo.VcoinWalletDO;
import com.laicunba.service.ActivityService;
import com.laicunba.service.CashCouponDefService;
import com.laicunba.service.FinancingTargetBidService;
import com.laicunba.service.FinancingTargetService;
import com.laicunba.service.MobileMessageService;
import com.laicunba.service.RateIncrCouponDefService;
import com.laicunba.service.RegistryService;
import com.laicunba.service.ScriptService;
import com.laicunba.service.UserService;
import com.laicunba.service.VcoinWalletService;
import com.laicunba.service.impl.helper.JsScriptRedisHelper;
import com.laicunba.service.mq.ActiveMQService;
import com.laicunba.util.AssertUtil;
import com.laicunba.util.CacheUtil;
import com.laicunba.util.StringUtil;

@Service
public class ScriptServiceImpl extends BaseServiceImpl implements ScriptService {
	private static Logger LOG = Logger.getLogger(ScriptServiceImpl.class);
	private static final String LOG_SPLIT = "==============================";
	private static final String SYS_ACT_SCRIPT_MAPPER="sys.act.script.mapper";
	private static Map<String, LinkedList<String>> LOG_MAP = new HashMap();

	@Autowired
	private ActiveMQService activeMQService;
	@Autowired
	private UserService userService;
	@Autowired
	private CashCouponDefService cashCouponDefService;
	@Autowired
	private RateIncrCouponDefService rateIncrCouponDefService;
	@Autowired
	private RegistryService registryService;
	@Autowired
	private MobileMessageService mobileMessageService;
	
	@Autowired
	private ActivityService activityService;
	@Autowired
	private FinancingTargetService financingTargetService;
	@Autowired
	private FinancingTargetBidService financingTargetBidService;
	@Autowired
	private VcoinWalletService vcoinWalletService;
	
	@Override
	public List<ScriptDO> list() {
		return queryDao.selectList("SCRIPT.list", null);
	}

	@Override
	public boolean listen(String id) {
		final ScriptDO scriptDO = get(ScriptDO.class, id);
		AssertUtil.assertNotNull(scriptDO, "SCRIPT_DO_NOT_EXIST");
		if ("jms-topic".equalsIgnoreCase(scriptDO.getInvokeType())) {
			return activeMQService.listenTopic(scriptDO.getId(), scriptDO.getInvokeCode(), new ActiveMQService.ListenCallBack() {

				@Override
				public String receive(Message msg) {
					System.out.println("xxxxxxxxxxx -->" + msg);
					return null;
				}
			});
		} else if ("jms-queue".equalsIgnoreCase(scriptDO.getInvokeType())) {
			return activeMQService.listenQueue(scriptDO.getId(), scriptDO.getInvokeCode(), new ActiveMQService.ListenCallBack() {

				@Override
				public String receive(Message msg) { 
					if(scriptDO.getBeginTime()!=null && scriptDO.getBeginTime().getTime()>System.currentTimeMillis()){
						return null;
					}
					if(scriptDO.getEndTime()!=null && scriptDO.getEndTime().getTime()<System.currentTimeMillis()){
						return null;
					}
					if(!"product".equalsIgnoreCase(scriptDO.getStatus())){
						 
						try {
							JsonMessage jsonMsg = new JsonMessage((TextMessage) msg);
							if(!jsonMsg.getBoolean("test")){
								return null;
							}
						} catch (Exception e) { 
							e.printStackTrace();
						}
						
					}
					ScriptEngineManager script = new ScriptEngineManager();
					ScriptEngine js = script.getEngineByName("js");
					JsonMessage jsonMessage = new JsonMessage((TextMessage) msg);
					js.getContext().setAttribute("log", new StringBuffer(), ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("userService", userService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("cashCouponDefService", cashCouponDefService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("rateIncrCouponDefService", rateIncrCouponDefService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("msg", jsonMessage, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("mobileMessageService", mobileMessageService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("redis", new JsScriptRedisHelper(), ScriptContext.ENGINE_SCOPE);
					
					js.getContext().setAttribute("activityService", activityService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("financingTargetService", financingTargetService, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("vcoinWalletService", vcoinWalletService, ScriptContext.ENGINE_SCOPE);
//					js.getContext().setAttribute("financingTargetBidService", financingTargetBidService, ScriptContext.ENGINE_SCOPE);
//					js.getContext().setAttribute("financingTargetBidDO", FinancingTargetBidDO.class, ScriptContext.ENGINE_SCOPE);
					FinancingTargetBidDO financingTargetBidDO = financingTargetBidService.get(FinancingTargetBidDO.class, jsonMessage.getString("id"));
					js.getContext().setAttribute("bidDO", financingTargetBidDO, ScriptContext.ENGINE_SCOPE);
					String userId = "";
					if (financingTargetBidDO != null) {
						userId = financingTargetBidDO.getBidUserId();
					}
					js.getContext().setAttribute("isFirstBuy", financingTargetBidService.isFirstBid(userId), ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("userDO", UserDO.class, ScriptContext.ENGINE_SCOPE);
					js.getContext().setAttribute("activityDO", ActivityDO.class, ScriptContext.ENGINE_SCOPE);
					
					try {
						js.eval(scriptDO.getScriptCode());
						StringBuffer logInfo = (StringBuffer) js.get("log");
						if (logInfo != null && StringUtil.isNotEmpty(logInfo.toString())) {
							LOG.info(logInfo.toString());
							if (!LOG_MAP.containsKey(scriptDO.getId())) {
								LOG_MAP.put(scriptDO.getId(), new LinkedList<String>());
							}
							LOG_MAP.get(scriptDO.getId()).add("--------------------------------------\r\n" + new Date() + "\r\n" + logInfo.toString());
							if(LOG_MAP.get(scriptDO.getId()).size()>100){
								while(LOG_MAP.get(scriptDO.getId()).size()>10){
									LOG_MAP.get(scriptDO.getId()).poll();
								}
							}
							
						}
					} catch (Exception e) {
						e.printStackTrace();
						if (!LOG_MAP.containsKey(scriptDO.getId())) {
							LOG_MAP.put(scriptDO.getId(), new LinkedList<String>());
						}
						LOG_MAP.get(scriptDO.getId()).add("--------------------------------------\r\n"+e.getMessage());
					}
					LOG.info("get queue message :" + msg);
					return null;
				}
			});
		}else if("act-join-event".equalsIgnoreCase(scriptDO.getInvokeType())){ 
			//clear old mapper setting
			Map<String, String>  map = CacheUtil.hgetAll(SYS_ACT_SCRIPT_MAPPER);
			if(map!=null){
				for(String actKey:map.keySet()){
					if(scriptDO.getId().equals(map.get(actKey))){
						CacheUtil.hdel(SYS_ACT_SCRIPT_MAPPER, actKey);
						LOG.info(String.format("取消活动 [%s]  -> 脚本 [%s] 的监听", actKey,scriptDO.getId()));
					}
				}
			}
			
			//create new mapper setting
			for(String  actKey :scriptDO.getInvokeCode().split(",")){
				actKey=actKey.trim();
				if(StringUtil.isEmpty(actKey)){
					continue;
				} 
				CacheUtil.hset(SYS_ACT_SCRIPT_MAPPER, actKey, scriptDO.getId());
				LOG.info(String.format("绑定活动 [%s]  -> 脚本 [%s] 的监听", actKey,scriptDO.getId()));
			}
			return true;
		}
		return false;
	}
	
	@Override
	public boolean stopListen(String id) {
		ScriptDO scriptDO = get(ScriptDO.class, id);
		AssertUtil.assertNotNull(scriptDO, "SCRIPT_DO_NOT_EXIST");
		activeMQService.stopListen(scriptDO.getId());
		//clear old mapper setting
		Map<String, String>  map = CacheUtil.hgetAll(SYS_ACT_SCRIPT_MAPPER);
		if(map!=null){
			for(String actKey:map.keySet()){
				if(scriptDO.getId().equals(map.get(actKey))){
					CacheUtil.hdel(SYS_ACT_SCRIPT_MAPPER, actKey);
					LOG.info(String.format("取消活动 [%s]  -> 脚本 [%s] 的监听", actKey,scriptDO.getId()));
				}
			}
		} 
	 
		return true;
	}

	@Override
	public Set<String> findJmsListenScriptIds() {
		return activeMQService.getAllSession().keySet();
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public Set<String> findActListenScriptIds() {
		Map<String, String>  map = CacheUtil.hgetAll(SYS_ACT_SCRIPT_MAPPER);
		HashSet set = new HashSet(); 
		if(map==null){
			return set;
		} 
		set.addAll(map.values());
		return set;
	}

	@Override
	public Map<String, LinkedList<String>> log() {
		return LOG_MAP;
	}

	@Override
	public boolean executeScript(UserDO userDO, ActivityDO activityDO, String subActId) {
		String  scriptId = CacheUtil.hget(SYS_ACT_SCRIPT_MAPPER, activityDO.getId().concat(".").concat(subActId));
		if(StringUtil.isEmpty(scriptId)){
			return false;
		}
		ScriptDO  scriptDO = get(ScriptDO.class, scriptId);
		if(scriptDO==null){
			return false;
		}
		if(scriptDO.getBeginTime()!=null && scriptDO.getBeginTime().getTime()>System.currentTimeMillis()){
			return false;
		}
		if(scriptDO.getEndTime()!=null && scriptDO.getEndTime().getTime()<System.currentTimeMillis()){
			return false;
		}
		if(!"product".equalsIgnoreCase(scriptDO.getStatus())){
			 
			try {
				RegistryDO  registryDO = registryService.get(RegistryDO.class, "sys.script.test."+scriptId);
				if(registryDO==null||StringUtil.isEmpty(registryDO.getVal())){
					return false;
				}
				if(!registryDO.getVal().contains(userDO.getId())){
					return false;
				}
			} catch (Exception e) { 
				e.printStackTrace();
			}
			
		}
		ScriptEngineManager script = new ScriptEngineManager();
		ScriptEngine js = script.getEngineByName("js");
		js.getContext().setAttribute("log", new StringBuffer(), ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("userService", userService, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("cashCouponDefService", cashCouponDefService, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("rateIncrCouponDefService", rateIncrCouponDefService, ScriptContext.ENGINE_SCOPE); 
		js.getContext().setAttribute("userDO", userDO, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("activityDO", activityDO, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("subActId", subActId, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("mobileMessageService", mobileMessageService, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("redis", new JsScriptRedisHelper(), ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("vcoinWalletService", vcoinWalletService, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("vcoinWalletDO", VcoinWalletDO.class, ScriptContext.ENGINE_SCOPE);
		js.getContext().setAttribute("cashCouponDefDO", CashCouponDefDO.class, ScriptContext.ENGINE_SCOPE);
		
		String returnCode=null;
		try {
			js.eval(scriptDO.getScriptCode());
			StringBuffer logInfo = (StringBuffer) js.get("log");
			if (logInfo != null && StringUtil.isNotEmpty(logInfo.toString())) { 
				appendLog(scriptDO.getId(),LOG_SPLIT);
				appendLog(scriptDO.getId(),new Date().toString());
				appendLog(scriptDO.getId(),logInfo.toString());
			}
			returnCode= (String) js.get("return_code");
		} catch (Exception e) {  
			appendLog(scriptDO.getId(),LOG_SPLIT);
			appendLog(scriptDO.getId(),new Date().toString());
			appendLog(scriptDO.getId(),e.getMessage());
		}  
		AssertUtil.assertTrue(StringUtil.isEmpty(returnCode)||"success".equalsIgnoreCase(returnCode), returnCode);
		return true;
	}
 
	private void appendLog(String scriptId ,String logStr) {	
		if (!LOG_MAP.containsKey(scriptId)) {
			LOG_MAP.put(scriptId, new LinkedList<String>());
		}
			LOG_MAP.get(scriptId).add(logStr+"\r\n");
	}
}
